{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Arbitrary Linear Transformation"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This model shows that any linear transformation between ensembles can be easily computed by selecting an appropriate value for the \"transform\". It provides an example of computing linear transformations using vector representations.\n",
    "\n",
    "Network diagram:\n",
    "\n",
    "      [input - 2D] ---> (x - 2d) ---> (z - 3D)\n",
    "\n",
    "A two-dimensional input signal is first fed into a two-dimensional neuronal ensemble `x` , which then projects it on to another three-dimensional ensemble `z`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "# Setup the environment\n",
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "%matplotlib inline\n",
    "\n",
    "import nengo"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Create the Model"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This model contains the parameters as described in the book. Setting the transform equal to the 'weight_matrix' is analogous to entering the weights in the \"2 to 3 Coupling Matrix\" window in Nengo 1.4 GUI as described in the book. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "# Create a 'model' object to which we can add ensembles, connections, etc.  \n",
    "model = nengo.Network(label=\"Arbitrary Linear Transformation\")\n",
    "with model:\n",
    "    # Two-dimensional input signal with constant values of 0.5 and -0.5 in two dimensions\n",
    "    input = nengo.Node(lambda t: [.5,-.5])\n",
    "      \n",
    "    # Ensembles with 200 LIF neurons having dimentions 2 and 3\n",
    "    x = nengo.Ensemble(200, dimensions=2)\n",
    "    z = nengo.Ensemble(200, dimensions=3)\n",
    "       \n",
    "    # Connect the input to ensemble x\n",
    "    nengo.Connection(input, x)\n",
    "    \n",
    "    # Connect ensemble x to ensemble z using a weight matrix\n",
    "    weight_matrix = [[0.0, 1.0], [1.0, 0.0], [0.5, 0.5]]   \n",
    "    nengo.Connection(x, z, transform = weight_matrix)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Run the Model"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "# Import the nengo_gui visualizer to run and visualize the model.\n",
    "from nengo_gui.ipython import IPythonViz\n",
    "IPythonViz(model, \"ch3-arbitrary-linear.py.cfg\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Press the play button in the visualizer to run the simulation. You should see the graphs as shown in the figure below.\n",
    "\n",
    "The graphs show a two-dimesional input linearly projected on to a two-dimensional ensemble of neurons (`x`), which further linearly projects it on to a three-dimesional neuronal ensemble (`z`). You can use the sliders to change the input values provided by the input node."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "from IPython.display import Image\n",
    "Image(filename='ch3-arbitrary-linear.png')"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 2",
   "language": "python",
   "name": "python2"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 2
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython2",
   "version": "2.7.10"
  },
  "widgets": {
   "state": {},
   "version": "1.1.2"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 0
}
